// Promise 状态常量
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
// Promise 解析过程
function resolvePromise(promise, x, resolve, reject) {
  // 防止循环引用
  if (promise === x) {
    return reject(new TypeError('The promise and the return value are the same'));
  }
  // 防止多次调用
  let called = false;
  // x 为对象或者函数
  if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
    try {
      // 获取 x.then 的值
      let then = x.then;
      // 如果 then 是函数，就默认是 Promise 了
      if (typeof then === 'function') {
        then.call(x, (y) => {
          // 成功和失败只能调用一个
          if (called) return;
          called = true;
          // resolve 的结果依旧是 Promise 那就继续解析
          resolvePromise(promise, y, resolve, reject);
        }, (r) => {
          // 成功和失败只能调用一个
          if (called) return;
          called = true;
          reject(r);
        });
      } else {
        resolve(x);
      }
    } catch(e) {
      // 也属于失败
      if (called) return;
      called = true;
      reject(e);
    }
  } else {
    resolve(x);
  }
}
class MyPromise {
  constructor(executor) {
    this.state = PENDING;
    this.value = undefined;
    this.reason = undefined;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];
    // 成功
    const resolve = value => {
      if (this.state === PENDING) {
        this.state = FULFILLED;
        this.value = value;
        // 依次执行成功回调
        this.onFulfilledCallbacks.forEach(fn => fn(value));
      }
    };

    const reject = reason => {
      if (this.state === PENDING) {
        this.state = REJECTED;
        this.reason = reason;
        // 依次执行失败回调
        this.onRejectedCallbacks.forEach(fn => fn(reason));
      }
    }
    try {
      executor(resolve, reject);
    } catch(e) {
      reject(e);
    }
  }

  then(onFulfilled,onRejected){
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
    onRejected = typeof onRejected === 'function' ? onRejected: err => { throw err };
    const promise2 = new MyPromise((resolve, reject) => {
      if(this.state === FULFILLED){
        queueMicrotask(() => {
          try {
            const x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch(e) {
            reject(e);
          }
        });
      }
      if(this.state === REJECTED){
        queueMicrotask(() => {
          try {
            const x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch(e) {
            reject(e);
          }
        });
      }
      if(this.state === PENDING){
        this.onFulfilledCallbacks.push(value => {
          queueMicrotask(() => {
            try {
              const x = onFulfilled(value);
              resolvePromise(promise2, x, resolve, reject);
            } catch(e) {
              reject(e);
            }
          });
        });
        this.onRejectedCallbacks.push(reason => {
          queueMicrotask(() => {
            try {
              const x = onRejected(reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch(e) {
              reject(e);
            }
          });
        });
      }
    })
    return promise2;
  }

  catch(onRejected) {
    return this.then(null, onRejected);
  }

  static resolve(value) {
    return new MyPromise((resolve, reject) => {
      resolve(value);
    });
  }

  static reject(reason) {
    return new MyPromise((resolve, reject) => {
      reject(reason);
    });
  }

  static all(promises) {
    return new MyPromise((resolve, reject) => {
      let count = 0;
      let result = [];
      for (let i = 0; i < promises.length; i++) {
        promises[i].then(value => {
          result[i] = value;
          if (++count === promises.length) {
            resolve(result);
          }
        }, reason => {
          reject(reason);
        });
      }
    });
  }

  static race(promises) {
    return new MyPromise((resolve, reject) => {
      for (let i = 0; i < promises.length; i++) {
        promises[i].then(value => {
          resolve(value);
        }, reason => {
          reject(reason);
        });
      }
    });
  }
  static deferred() {
    var result = {};
    result.promise = new MyPromise(function(resolve, reject) {
      result.resolve = resolve;
      result.reject = reject;
    });
    return result;
  }
  static allSettled(promises) {
    return new MyPromise((resolve, reject) => {
      let count = 0;
      let result = [];
      for (let i = 0; i < promises.length; i++) {
        promises[i].then(value => {
          result[i] = { status: 'fulfilled', value };
          if (++count === promises.length) {
            resolve(result);
          }
        }, reason => {
          result[i] = { status: 'rejected', reason };
          if (++count === promises.length) {
            resolve(result);
          }
        });
      }
    });
  }
}

export default MyPromise;
